// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef TOOLS_JSON_SCHEMA_COMPILER_UTIL_H_
#define TOOLS_JSON_SCHEMA_COMPILER_UTIL_H_

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/format_macros.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"

namespace json_schema_compiler {

namespace util {

// Populates the item |out| from the value |from|. These are used by template
// specializations of |Get(Optional)ArrayFromList|.
bool PopulateItem(const base::Value& from, base::Value& out);

bool PopulateItem(const base::Value& from, int& out);
bool PopulateItem(const base::Value& from, int& out, std::u16string& error);
bool PopulateItem(const base::Value& from, bool& out);
bool PopulateItem(const base::Value& from, bool& out, std::u16string& error);
bool PopulateItem(const base::Value& from, double& out);
bool PopulateItem(const base::Value& from, double& out, std::u16string& error);
bool PopulateItem(const base::Value& from, std::string& out);
bool PopulateItem(const base::Value& from,
                  std::string& out,
                  std::u16string& error);
bool PopulateItem(const base::Value& from, std::vector<uint8_t>& out);
bool PopulateItem(const base::Value& from,
                  std::vector<uint8_t>& out,
                  std::u16string& error);
bool PopulateItem(const base::Value& from,
                  base::Value& out,
                  std::u16string& error);

// This template is used for types generated by tools/json_schema_compiler.
template <class T>
bool PopulateItem(const base::Value& from, T& out) {
  T obj;
  if (!T::Populate(from, obj)) {
    return false;
  }
  out = std::move(obj);
  return true;
}

// This template is used for types generated by tools/json_schema_compiler with
// error generation enabled.
template <class T>
bool PopulateItem(const base::Value& from, T& out, std::u16string& error) {
  T obj;
  if (!T::Populate(from, obj, error)) {
    return false;
  }
  out = std::move(obj);
  return true;
}

// Populates |out| with |list|. Returns false if there is no list at the
// specified key or if the list has anything other than |T|.
template <class T>
bool PopulateArrayFromList(const base::Value::List& list, std::vector<T>& out) {
  out.clear();
  out.reserve(list.size());
  T item;
  for (const auto& value : list) {
    if (!PopulateItem(value, item)) {
      return false;
    }
    // T might not be movable, but in that case it should be copyable, and this
    // will still work.
    out.push_back(std::move(item));
  }

  return true;
}

// Populates |out| with |list|. Returns false and sets |error| if there is no
// list at the specified key or if the list has anything other than |T|.
template <class T>
bool PopulateArrayFromList(const base::Value::List& list,
                           std::vector<T>& out,
                           std::u16string& error) {
  out.clear();
  out.reserve(list.size());
  T item;
  std::u16string item_error;
  for (size_t i = 0; i < list.size(); ++i) {
    if (!PopulateItem(list[i], item, item_error)) {
      DCHECK(error.empty());
      error = base::ASCIIToUTF16(
          base::StringPrintf("Parsing array failed at index %" PRIuS ": %s", i,
                             base::UTF16ToASCII(item_error).c_str()));
      return false;
    }
    out.push_back(std::move(item));
  }

  return true;
}

// Creates a new vector containing |list| at |out|. Returns
// true on success or if there is nothing at the specified key. Returns false
// if anything other than a list of |T| is at the specified key.
template <class T>
bool PopulateOptionalArrayFromList(const base::Value::List& list,
                                   std::optional<std::vector<T>>& out) {
  std::vector<T> populated;
  if (!PopulateArrayFromList(list, populated)) {
    return false;
  }
  out = std::move(populated);
  return true;
}

template <class T>
bool PopulateOptionalArrayFromList(const base::Value::List& list,
                                   std::optional<std::vector<T>>& out,
                                   std::u16string& error) {
  std::vector<T> populated;
  if (!PopulateArrayFromList(list, populated, error)) {
    return false;
  }
  out = std::move(populated);
  return true;
}

// Appends a Value newly created from |from| to |out|. These used by template
// specializations of |Set(Optional)ArrayToList|.
void AddItemToList(const int from, base::Value::List& out);
void AddItemToList(const bool from, base::Value::List& out);
void AddItemToList(const double from, base::Value::List& out);
void AddItemToList(const std::string& from, base::Value::List& out);
void AddItemToList(const std::vector<uint8_t>& from, base::Value::List& out);
void AddItemToList(const base::Value& from, base::Value::List& out);
void AddItemToList(const base::Value::Dict& from, base::Value::List& out);

// This template is used for types generated by tools/json_schema_compiler.
template <class T>
void AddItemToList(const std::unique_ptr<T>& from, base::Value::List& out) {
  out.Append(from->ToValue());
}

// This template is used for types generated by tools/json_schema_compiler.
template <class T>
void AddItemToList(const T& from, base::Value::List& out) {
  out.Append(from.ToValue());
}

// Returns a list matching the contents of |from|. Requires PopulateItem to be
// implemented for |T|.
template <class T>
base::Value::List CreateValueFromArray(const std::vector<T>& from) {
  base::Value::List list;
  for (const T& item : from) {
    AddItemToList(item, list);
  }
  return list;
}

template <class T>
void AppendToContainer(base::Value::List& container, T&& value) {
  container.Append(std::forward<T>(value));
}

template <class C, class T>
void AppendToContainer(std::vector<C>& container, T&& value) {
  container.emplace_back(std::forward<T>(value));
}

}  // namespace util
}  // namespace json_schema_compiler

#endif  // TOOLS_JSON_SCHEMA_COMPILER_UTIL_H_
